﻿#region codejockey.net Copyright Banner
/***************************************************************\
 * Copyright 2010 codejockey.net.                                *
 *                                                               *
 * This code is property of codejockey.net and cannot be used    *
 * without explicit permission given by a codejockey.net         *
 * Representative.  For details contact: source@codejockey.net   *
 \***************************************************************/
#endregion

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;

using log4net;
using log4net.Config;

using IO = System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

using codejockey.Shared.Win32;
using codejockey.Shared.Text;
using codejockey.Shared.Text.ReplacementTokens;

using codejockey.BingPaper.Utils;
using codejockey.BingPaper.Configuration;

namespace codejockey.BingPaper
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow
  {
    #region Default Values

    private const string AppTitle = "BingPaper";
    private const string AppTitleWithCountryCode = AppTitle + " - [{0}]";
    private const string DefaultBaseUrl = "http://www.bing.com";
    private const int DefaultLengthLimit = 255;

    #endregion

    #region CommandLine Args

    const string SetWallPaperArg = "setwallpaper";
    const string FilePathArg = "filepath";
    const string FileNameArg = "filename";
    const string WallPaperStyleArg = "wallpaperstyle";
    const string HelpArg = "help";
    const string HelpArgAlt = "?";
    const string UseConfigArg = "useConfig";
    const string TokenHelpArg = "tokenList";
    const string DayIndexArg = "dayIndex";
    const string CountryCodeArg = "countryCode";

    #endregion

    private string _imageName;
    private string _fileName;
    private string _filePath;
    private bool _setWallPaper = true;
    private WallpaperStyle _wallPaperStyle = WallpaperStyle.Stretched;
    private TextBox _selectedTextBox;
    private Brush _fileNameBrush;
    private Brush _filePathBrush;
    private TimeSpan _refreshInterval;
    readonly DispatcherTimer _dispatcherTimer = new DispatcherTimer();
    private static ILog _log;

    //private JumpList jumpList = null;

    private static int LengthLimit
    {
      get
      {
        // NOTE: Because "Length Limit" is an OS limitation it's stored outside of the BingPaper custom configuration details.
        string lengthLimitTemp = ConfigurationManager.AppSettings["lengthLimit"];

        _log.DebugFormat("lengthLimit present in the config file, using it's value which is: [{0}]", lengthLimitTemp);

        short limit = DefaultLengthLimit;

        if (!string.IsNullOrEmpty(lengthLimitTemp))
          Int16.TryParse(lengthLimitTemp, out limit);

        _log.DebugFormat("Final LengthLimit value: [{0}]", limit);

        return limit;
      }
    }

    private BingPaperConfigSection _bingPaperConfig;

    private BingPaperConfigSection BingPaperConfig
    {
      get
      {
        if (_bingPaperConfig == null)
          LoadSettings();

        return _bingPaperConfig;
      }
    }

    const string WelcomeTitle = "BingPaper v1.0 written by: Eric Brown";

    public MainWindow()
    {
      InitializeComponent();

      XmlConfigurator.Configure();
      _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

      PopulateTokens();
      PopulateCountryCodes();

      if (App.CommandLineArgs != null && App.CommandLineArgs.Count > 0)
      {
        if (ProcessCommandLineArgs())
          Application.Current.Shutdown();
        Hide();
      }
      else
      {
        PopulateRefreshIntervals();
        lengthLabel.Content = Convert.ToString(LengthLimit);
        SetCurrentBingImage(Convert.ToInt16(daySlider.Value));
      }

      SetTitle();
    }

    private void PopulateRefreshIntervals()
    {
      const int minuteLowerLimit = 5;

      const int hours = 24;
      const int minutes = 59;

      var hourList = new List<int>();
      var minuteList = new List<int>();

      for (int i = hours; i >= 0; i--)
        hourList.Add(i);

      for (int i = minutes; i >= 0; i--)
        minuteList.Add(i);

      var timeSpans = (from curHour in hourList
                       from curMinute in minuteList
                       where curHour != 0 || curMinute >= minuteLowerLimit
                       select new TimeSpan(curHour, curMinute, 0)).ToList();

      foreach (TimeSpan curTimeSpan in timeSpans)
        refreshIntervalComboBox.Items.Add(curTimeSpan);
    }

    private static string GenerateHelp()
    {
      var sb = new StringBuilder();
      sb.AppendLine(WelcomeTitle);
      sb.AppendLine();
      sb.AppendLine("Useage: ");
      sb.AppendFormat("/{0}:[true/false] - Set the wallpaper to the current Bing image.{1}", SetWallPaperArg, Environment.NewLine);
      sb.AppendFormat("/{0}:[save as name] - File name to save the image as.{1}", FileNameArg, Environment.NewLine);
      sb.AppendFormat("/{0}:[path to save file to] - Path to save the image to.{1}  Note: Tokens are valid for file and path values.{1}", FilePathArg, Environment.NewLine);
      sb.AppendFormat("/{0} Uses the configuration file settings. (Overrides ALL){1}", UseConfigArg, Environment.NewLine);
      sb.AppendFormat("/{0} Displays this help message.{1}", HelpArg, Environment.NewLine);
      sb.AppendFormat("/{0} Displays current list of valid tokens.{1}", TokenHelpArg, Environment.NewLine);
      sb.AppendFormat("/{0} [0-7] Is the current day to grab.{1}", DayIndexArg, Environment.NewLine);
      sb.AppendFormat("/{0} [two letter country code] Grabs the image from this country code.{1}", CountryCodeArg, Environment.NewLine);
      sb.AppendLine();
      sb.AppendLine("Examples of usage:");
      sb.AppendFormat(@"  BingPaper /{0}:""C:\Pictures\Bing Pics\2009""", FileNameArg);
      sb.AppendLine();
      sb.AppendLine("    Downloads today's Bing image and saves it to the given path.");
      sb.AppendLine();
      sb.AppendFormat(@"  BingPaper /{0} (recommended)", UseConfigArg);
      sb.AppendLine();
      sb.AppendLine("    Download's today's Bing image using setting in the config file.");

      return sb.ToString();
    }

    private bool ProcessCommandLineArgs()
    {
      bool result;

      if (!IsHelpRequested())
      {
        string useConfigTemp = App.CommandLineArgs[UseConfigArg];
        bool useConfig = true;

        GenerateTokenHelp();

        if (!string.IsNullOrEmpty(useConfigTemp))
          useConfig = Convert.ToBoolean(useConfigTemp);

        // NOTE: If useConfig the configuration settings override any parameters passed in.      
        if (useConfig)
        {
          _filePath = BingPaperConfig.Settings.Archive.FilePath;
          _fileName = BingPaperConfig.Settings.Archive.FileName;
          _setWallPaper = BingPaperConfig.Settings.WallPaper.SetWallPaper;
          _wallPaperStyle = GetWallPaperStyleFromString(BingPaperConfig.Settings.WallPaper.WallPaperStyle);
        }
        else
        {
          _filePath = App.CommandLineArgs[FilePathArg];
          _fileName = App.CommandLineArgs[FileNameArg];

          string setWallPaperTemp = App.CommandLineArgs[SetWallPaperArg];

          if (!string.IsNullOrEmpty(setWallPaperTemp))
            _setWallPaper = Convert.ToBoolean(setWallPaperTemp);

          string wallPaperStyleTemp = App.CommandLineArgs[WallPaperStyleArg];

          if (!string.IsNullOrEmpty(wallPaperStyleTemp))
            _wallPaperStyle = GetWallPaperStyleFromString(wallPaperStyleTemp);
        }

        result = !string.IsNullOrEmpty(_filePath) && !string.IsNullOrEmpty(_fileName);

        if (result)
        {
          var imageParms = new BingImageParams();

          string countryCode = App.CommandLineArgs[CountryCodeArg];

          if (!string.IsNullOrEmpty(countryCode) && countryCode.Length > 2)
          {
            // Assuming the user is passing in the "Long" name of the country rather than the 2 letter code.
            foreach (KeyValuePair<string, string> curItem in BingUtils.CountryCodes)
            {
              if (curItem.Key.Equals(countryCode))
              {
                BingUtils.CurrentCountryCode = curItem.Value;
                break;
              }
            }
          }
          else
            BingUtils.CurrentCountryCode = countryCode;

          imageParms.CountryCode = BingUtils.CurrentCountryCode;

          string dayIndex = App.CommandLineArgs[DayIndexArg];

          imageParms.DayIndex = !string.IsNullOrEmpty(dayIndex) ? Convert.ToInt16(dayIndex) : 0;

          imageParms.Path = _filePath;
          imageParms.File = _fileName;

          BingUtils.LoadTokens();

          BingImage image = BingUtils.GetBingImage(imageParms);
          string bingImageTemp = image.ImageFileName;

          if (_setWallPaper)
            Wallpaper.Set(bingImageTemp, _wallPaperStyle);
        }
      }
      else
      {
        MessageBox.Show(GenerateHelp());
        result = true;
      }

      return result;
    }

    private static void GenerateTokenHelp()
    {
      if (!string.IsNullOrEmpty(App.CommandLineArgs[TokenHelpArg]))
      {
        var tokenText = new StringBuilder();
        tokenText.AppendLine("Current valid tokens:");

        foreach (KeyValuePair<string, IReplacementToken> curItem in TokenManager.Tokens)
          tokenText.AppendFormat(string.Format("{0}={1}{2}", curItem.Key, curItem.Value.ReplaceValue, Environment.NewLine));

        MessageBox.Show(tokenText.ToString());
      }
    }

    private static bool IsHelpRequested()
    {
      bool helpRequest = false;

      string displayHelpTemp = App.CommandLineArgs[HelpArg];
      string displayHelpTemp2 = App.CommandLineArgs[HelpArgAlt];

      if (!string.IsNullOrEmpty(displayHelpTemp))
        helpRequest = Convert.ToBoolean(displayHelpTemp);

      if (!helpRequest && !string.IsNullOrEmpty(displayHelpTemp2))
        helpRequest = Convert.ToBoolean(displayHelpTemp2);

      return helpRequest;
    }

    private static WallpaperStyle GetWallPaperStyleFromString(string wallPaperStyle)
    {
      return (WallpaperStyle)Enum.Parse(typeof(WallpaperStyle), wallPaperStyle);
    }

    private void LoadSettings()
    {
      if (_log.IsInfoEnabled)
        _log.Info("Loading settings...");

      _bingPaperConfig = ConfigurationManager.GetSection(BingPaperConfigSection.SectionName) as BingPaperConfigSection;

      if (_bingPaperConfig != null)
      {
        if (_log.IsDebugEnabled)
          _log.Debug("_bingPaperConfig correctly hydrated based on the applications configuration file... Using values from the config file.");

        _fileName = _bingPaperConfig.Settings.Archive.FileName;
        fileNameTextBox.Text = _bingPaperConfig.Settings.Archive.FileName;
        _filePath = _bingPaperConfig.Settings.Archive.FilePath;
        filePathTextBox.Text = _bingPaperConfig.Settings.Archive.FilePath;
        setAsWallPaperCheckBox.IsChecked = _bingPaperConfig.Settings.WallPaper.SetWallPaper;
        wallpaperStyle.Text = _bingPaperConfig.Settings.WallPaper.WallPaperStyle;
        autoRefreshCheckBox.IsChecked = _bingPaperConfig.Settings.AutoRefresh.Enabled;
        _refreshInterval = _bingPaperConfig.Settings.AutoRefresh.RefreshInterval;
        refreshIntervalComboBox.SelectedItem = _bingPaperConfig.Settings.AutoRefresh.RefreshInterval;

        if (!refreshIntervalComboBox.Items.Contains(_bingPaperConfig.Settings.AutoRefresh.RefreshInterval))
        {
          refreshIntervalComboBox.Items.Add(_refreshInterval);
          refreshIntervalComboBox.SelectedItem = _refreshInterval;
        }

        if (!string.IsNullOrWhiteSpace(_bingPaperConfig.Settings.DefaultCountry))
          BingUtils.CurrentCountryCode = _bingPaperConfig.Settings.DefaultCountry;
      }
      else
      {
        if (_log.IsErrorEnabled)
          _log.ErrorFormat("Unable to load the configuration settings.  The configuration file may be missing the section: [{0}].", BingPaperConfigSection.SectionName);

        throw new Exception(string.Format("The configuration file is missing the section: {0}.", BingPaperConfigSection.SectionName));
      }
    }

    private void SaveSettings()
    {
      if (IsSaveNeeded())
      {
        MessageBoxResult result = MessageBox.Show("Save settings?", "Question", MessageBoxButton.YesNo, MessageBoxImage.Question);

        if (result == MessageBoxResult.Yes)
        {
          System.Configuration.Configuration bingConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
          var bingConfigSettings = bingConfig.GetSection(BingPaperConfigSection.SectionName) as BingPaperConfigSection;

          if (bingConfigSettings != null)
          {
            bingConfigSettings.Settings.Archive.FileName = fileNameTextBox.Text;
            bingConfigSettings.Settings.Archive.FilePath = filePathTextBox.Text;
            bingConfigSettings.Settings.WallPaper.WallPaperStyle = wallpaperStyle.Text;

            if (setAsWallPaperCheckBox.IsChecked != null)
              bingConfigSettings.Settings.WallPaper.SetWallPaper = setAsWallPaperCheckBox.IsChecked.Value;

            bingConfigSettings.Settings.AutoRefresh.RefreshInterval = _refreshInterval;

            if (autoRefreshCheckBox.IsChecked != null)
              bingConfigSettings.Settings.AutoRefresh.Enabled = autoRefreshCheckBox.IsChecked.Value;

            bingConfigSettings.Settings.DefaultCountry = BingUtils.CountryCodes[countryCodesComboBox.Text];

            if (string.IsNullOrEmpty(BingPaperConfig.Settings.BaseURL))
              bingConfigSettings.Settings.BaseURL = DefaultBaseUrl;
          }

          bingConfig.Save(ConfigurationSaveMode.Modified);
        }
      }
    }

    private void SetCurrentBingImage(int dayIndex)
    {
      string tempFile = IO.Path.GetTempFileName();

      BingImage currentBingImage = BingUtils.GetBingImage(BingUtils.CurrentCountryCode, dayIndex);

      //if (BingPaperConfig != null)
      //  tempFile = BingUtils.GetCurrentBingImage(new Uri(BingPaperConfig.Settings.BaseURL), tempFolder);

      if (!string.IsNullOrEmpty(tempFile))
        bingImage.Source = new BitmapImage(currentBingImage.ImageUrl);
    }

    private void PopulateTokens()
    {
      BingUtils.LoadTokens();

      tokenListBox.Items.Clear();

      foreach (KeyValuePair<string, IReplacementToken> curItem in TokenManager.Tokens)
        tokenListBox.Items.Add(string.Format("{0}={1}", curItem.Key, curItem.Value.ReplaceValue));
    }

    private void PopulateCountryCodes()
    {
      countryCodesComboBox.Items.Clear();

      foreach (KeyValuePair<string, string> curItem in BingUtils.CountryCodes)
        countryCodesComboBox.Items.Add(curItem.Key);

    }

    private void ButtonClick(object sender, RoutedEventArgs e)
    {
      GetImageAndSetWallpaper();
    }

    private bool IsSaveNeeded()
    {
      filePathTextBox.Text = StringUtils.AppendPathDelimeter(filePathTextBox.Text);

      bool saveNeeded = BingPaperConfig.Settings.Archive.FilePath != filePathTextBox.Text;

      if (!saveNeeded)
        saveNeeded = BingPaperConfig.Settings.Archive.FileName != fileNameTextBox.Text;

      if (!saveNeeded)
        saveNeeded = BingPaperConfig.Settings.WallPaper.SetWallPaper != setAsWallPaperCheckBox.IsChecked;

      if (!saveNeeded)
        saveNeeded = BingPaperConfig.Settings.AutoRefresh.RefreshInterval != _refreshInterval;

      if (!saveNeeded && autoRefreshCheckBox.IsChecked != null)
        saveNeeded = BingPaperConfig.Settings.AutoRefresh.Enabled != autoRefreshCheckBox.IsChecked.Value;

      if (!saveNeeded && !string.IsNullOrWhiteSpace(countryCodesComboBox.Text))
        saveNeeded = BingPaperConfig.Settings.DefaultCountry != BingUtils.CountryCodes[countryCodesComboBox.Text];

      return saveNeeded;
    }

    private void GetImageAndSetWallpaper()
    {
      _imageName = BingPaperConfig.Settings.Archive.FileName;
      _fileName = string.Empty;

      if (!filePathTextBox.Text.EndsWith(@"\"))
        filePathTextBox.Text = filePathTextBox.Text + @"\";

      if (BingPaperConfig.Settings.Archive.ArchiveImage)
      {
        var imageParams = new BingImageParams
                            {
                              DayIndex = Convert.ToInt16(daySlider.Value),
                              CountryCode = BingUtils.CurrentCountryCode
                            };

        if (!string.IsNullOrEmpty(filePathTextBox.Text))
          imageParams.Path = filePathTextBox.Text;

        if (!string.IsNullOrEmpty(_imageName))
          imageParams.File = fileNameTextBox.Text;

        var image = BingUtils.GetBingImage(imageParams);

        _fileName = image.ImageFileName;

        var tempPath = IO.Path.GetTempFileName();
        var tempFile = IO.Path.GetTempFileName();
        var tempImage = IO.Path.Combine(tempPath, tempFile);

        if (IO.File.Exists(tempImage))
          IO.File.Delete(tempImage);

        if (!string.IsNullOrEmpty(_fileName))
          IO.File.Copy(_fileName, tempFile);

        if (!string.IsNullOrEmpty(tempImage))
        {
          bingImage.Source = new BitmapImage(new Uri(tempImage));

          if (setAsWallPaperCheckBox.IsChecked.HasValue && setAsWallPaperCheckBox.IsChecked.Value)
          {
            var style = (WallpaperStyle)Enum.Parse(typeof(WallpaperStyle), wallpaperStyle.Text, true);

            if (_fileName != null)
              Wallpaper.Set(new Uri(_fileName), style);
          }
        }
      }
    }

    private string GetPath()
    {
      string parsedFilePath = BingUtils.ParseTokens(filePathTextBox.Text);
      string parsedFileName = BingUtils.ParseTokens(fileNameTextBox.Text);

      return IO.Path.Combine(parsedFilePath, parsedFileName);
    }

    private void FileNameTextBoxTextChanged(object sender, TextChangedEventArgs e)
    {
      var path = GetPath();

      ToggleTextFields(path);

      SetResultLabel();
    }

    private void ToggleTextFields(string path)
    {
      int total = LengthLimit - path.Length;

      if (total <= 0)
      {
        filePathTextBox.Background = Brushes.DarkRed;
        fileNameTextBox.Background = Brushes.DarkRed;
        goButton.IsEnabled = false;
        _dispatcherTimer.IsEnabled = false;
      }
      else
      {
        goButton.IsEnabled = true;
        filePathTextBox.Background = _filePathBrush;
        fileNameTextBox.Background = _fileNameBrush;
        _dispatcherTimer.IsEnabled = true;
      }
    }

    private void SetResultLabel()
    {
      var path = GetPath();
      resultantPathLabel.Content = path;
      lengthLabel.Content = LengthLimit - path.Length;
    }

    private void TokenListBoxMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
      if (sender is ListBox)
      {
        int index = tokenListBox.SelectedItem.ToString().IndexOf('=');
        string selectedToken = tokenListBox.SelectedItem.ToString().Substring(0, index);

        if (_selectedTextBox != null)
        {
          _selectedTextBox.Text = _selectedTextBox.Text + selectedToken;
          _selectedTextBox.Focus();
          _selectedTextBox.CaretIndex = _selectedTextBox.Text.Length + 1;
        }
      }
    }

    private void FileNameTextBoxLostFocus(object sender, RoutedEventArgs e)
    {
      _selectedTextBox = fileNameTextBox;
    }

    private void FilePathTextBoxLostFocus(object sender, RoutedEventArgs e)
    {
      _selectedTextBox = filePathTextBox;
    }

    private void FilePathTextBoxLoaded(object sender, RoutedEventArgs e)
    {
      _filePathBrush = filePathTextBox.Background;
      _fileNameBrush = fileNameTextBox.Background;
    }

    private void WindowLoaded(object sender, RoutedEventArgs e)
    {
      LoadSettings();

      if (autoRefreshCheckBox.IsChecked != null)
        if (autoRefreshCheckBox.IsChecked.Value)
        {
          _dispatcherTimer.Tick += DispatcherTimerTick;
          _dispatcherTimer.Interval = _refreshInterval;

          SetRefreshLabel();

          _dispatcherTimer.Start();

          refreshCountdownLabel.Visibility = Visibility.Visible;
        }

      countryCodesComboBox.SelectedValue = BingUtils.CurrentCountryCodeKey;

      if (autoRefreshCheckBox.IsChecked != null)
        refreshIntervalComboBox.IsEnabled = autoRefreshCheckBox.IsChecked.Value;
    }

    private void SetRefreshLabel()
    {
      DateTime nextRefresh = DateTime.Now.Add(_refreshInterval);
      refreshCountdownLabel.Content = string.Format("Next Refresh At: {0}", nextRefresh.ToString("hh:mm:ss"));

      if (autoRefreshCheckBox.IsChecked != null)
        refreshCountdownLabel.Visibility = autoRefreshCheckBox.IsChecked.Value ? Visibility.Visible : Visibility.Hidden;
    }

    private void DispatcherTimerTick(object sender, EventArgs e)
    {
      if (autoRefreshCheckBox.IsChecked != null && autoRefreshCheckBox.IsChecked.Value)
      {
        PopulateTokens();
        SetResultLabel();
        GetImageAndSetWallpaper();
      }
      else
        _dispatcherTimer.Stop();

      SetRefreshLabel();
    }

    private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
    {
      SaveSettings();
    }

    private void AutoRefreshCheckBoxClick(object sender, RoutedEventArgs e)
    {
      if (autoRefreshCheckBox.IsChecked != null)
      {
        refreshIntervalComboBox.IsEnabled = autoRefreshCheckBox.IsChecked.Value;
        if (!autoRefreshCheckBox.IsChecked.Value)
        {
          _dispatcherTimer.Stop();
          refreshCountdownLabel.Visibility = Visibility.Hidden;
        }
        else
        {
          _dispatcherTimer.Start();
          SetRefreshLabel();
          refreshCountdownLabel.Visibility = Visibility.Visible;
        }
      }
    }

    private void RefreshIntervalComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (refreshIntervalComboBox.SelectedItem is TimeSpan)
      {
        _dispatcherTimer.Interval = (TimeSpan)refreshIntervalComboBox.SelectedItem;
        _refreshInterval = _dispatcherTimer.Interval;
        SetRefreshLabel();
      }
    }

    private void CountryCodesComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (e.AddedItems.Count > 0)
      {
        string selectedCountryKey = e.AddedItems[0].ToString();
        BingUtils.CurrentCountryCode = BingUtils.CountryCodes[selectedCountryKey];
        PopulateTokens();
        SetResultLabel();
        SetCurrentBingImage(Convert.ToInt16(daySlider.Value));
        SetTitle(BingUtils.CurrentCountryCode);
      }
    }

    private void SetTitle(string countryCode = null)
    {
      Title = !string.IsNullOrEmpty(countryCode) ? string.Format(AppTitleWithCountryCode, countryCode) : AppTitle;
    }

    private void DaySliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
      SetCurrentBingImage(Convert.ToInt16(e.NewValue));
      PopulateTokens();
      SetResultLabel();
    }
  }
}